﻿#include "Event.h"
#include <sys/eventfd.h>
#include <sys/select.h>
#include <unistd.h>
#include <stdio.h>
#include <errno.h>
// #include "log4z.h"
#include <assert.h>
#include "common.h"
// using namespace zsummer::log4z;

Event::Event(int cycle):
    m_startup(0)
{
    m_cycle = cycle;
    m_event = -1;
    assert(m_cycle >= 0);
}

Event::~Event()
{

}

int Event::start()
{
    if (m_startup) return -1;

    m_event = eventfd(0, 0);
    if (-1 == m_event) return -1;

    m_work = new std::thread(std::bind(&Event::DataProcess, this));
    m_startup = 1;

    return 0;
}

int Event::stop()
{
    if (!m_startup) return -1;

    m_startup = 0;
    WakeUpTask();

    if (m_work && m_work->joinable())
    {
        m_work->join();
    }
    
    close(m_event);
    m_event = -1;
    delete m_work; m_work = NULL;

    return 0;
}

int Event::ListenEvents(int handler, std::function<void(int, TypeEvent)> func)
{
    auto iter = m_func_table.find(handler);
    if (iter != m_func_table.end()) return -1;
    
    m_func_table.insert({handler, func});
    return 0;
}

int Event::DeleteEvents(int handler)
{
    auto iter = m_func_table.find(handler);
    if (iter == m_func_table.end()) return -1;

    m_func_table.erase(iter);
    return 0;
}

void Event::DataProcess()
{
    long long tempVal = 0, _max;
    fd_set fdEvent;
    struct timeval tv = { 0, 0 };

    while (m_startup)
    {
        tv.tv_sec = m_cycle / 1000;
        tv.tv_usec = (m_cycle - tv.tv_sec * 1000) * 1000 + 0;
        _max = 0;

        FD_ZERO(&fdEvent);
        FD_SET(m_event, &fdEvent);

        for (auto& elem : m_func_table)
        {
            _max = max(_max, elem.first);
            FD_SET(elem.first, &fdEvent);
        }
        _max = max(_max, m_event);

    again_select:
        int ret = select(_max + 1, &fdEvent, NULL, NULL, &tv); /* NULL, 0, or timeout */
        if (ret < 0)
        {
            if (errno == EINTR)
                goto again_select;

            LOGFMTE(
                "select error errno = %d(%s)", errno, strerror(errno)
            );
            for (auto& elem : m_func_table) elem.second(elem.first, TypeError);

            return;
        }
        else if (ret == 0)
        {
            this->CycleTask();
            continue;
        }

        for (auto& elem : m_func_table)
        {
            if (FD_ISSET(elem.first, &fdEvent)) elem.second(elem.first, TypeRead);
        }
        
        if (FD_ISSET(m_event, &fdEvent))  /* 暂时作为唤醒操作 */
        {
            uint64_t exitFlag = 0;
            if (sizeof(uint64_t) != read(m_event, &exitFlag, sizeof(uint64_t)))
            {
                break; /* 退出任务 */
            }
            continue;
        }
    }
}

int Event::WakeUpTask()
{
    uint64_t flag = 1;

    if (sizeof(uint64_t) != write(m_event, &flag, sizeof(uint64_t)))
    {
        LOGFMTD("Error sending notification : %s", strerror(errno));
        return -1;
    }
    return 0;
}

void Event::CycleTask()
{
    return;
}
